﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Windows.Forms;
using AuthoritExtensibility;
using WordCount.Properties;
using Ait = Authorit.API;

namespace WordCount
{
	/// <summary>
	/// The <see cref="WordCountForm"/> class is the main form for the plug-in.
	/// </summary>
	public partial class WordCountForm : Form
	{
		#region Fields (16)

		private Ait.ApplicationContext appContext;
		private int bookId;
		private Config config;
		private AuthoritEventArgs eventArgs;
		private bool keepClipboardDataAfterExit;
		private string libraryAlias;
		private List<int> objectIds;
		private string password;
		private Ait.Utilities.ReleaseStatesCollection releaseStates;
		private Ait.Services.Authentication svcAuthentication;
		private Ait.Services.Book svcBook;
		private Ait.Services.File svcFile;
		private Ait.Services.Hyperlink svcHyperlink;
		private Ait.Services.ObjectCommon svcObjectCommon;
		private Ait.Services.ReleaseState svcReleaseState;
		private Ait.Services.Topic svcTopic;
		private string username;

		#endregion Fields

		#region Constructors (2)

		/// <summary>
		/// The <see cref="WordCountForm(AuthoritEventArgs, Config)"/> initializes a new instance of the <see cref="WordCountForm"/> class.
		/// </summary>
		/// <param name="args">The instance of the <see cref="AuthoritExtensibility.AuthoritEventArgs"/> class to assign to the
		/// <see cref="WordCountForm.EventArgs"/> property</param>
		/// <param name="pluginConfig">Instance of the <see cref="Config"/> class for this instance of the <see cref="WordCountForm"/> class</param>
		public WordCountForm(AuthoritEventArgs args, Config pluginConfig)
		{
			this.bookId = args.CurrentBookId;
			this.config = pluginConfig;
			this.eventArgs = args;
			InitializeComponent();
			LoadProperties();
		}

		/// <summary>
		/// The <see cref="WordCountForm()"/> initializes a new instance of the <see cref="WordCountForm"/> class.
		/// </summary>
		public WordCountForm()
		{
			this.bookId = 0;
			InitializeComponent();
			LoadProperties();
		}

		#endregion Constructors

		#region Properties (1)

		/// <summary>
		/// The <see cref="EventArgs"/> property gets or sets the instance of <see cref="AuthoritEventArgs"/> for this instance of the <see cref="WordCountForm"/> class.
		/// </summary>
		public AuthoritEventArgs EventArgs
		{
			get { return eventArgs; }
			set { eventArgs = value; }
		}

		#endregion Properties

		#region Methods (7)

		// Private Methods (7) 

		/// <summary>
		/// The <see cref="GetReleaseStates()"/> method gets an instance of the <see cref="Authorit.API.Utilities.ReleaseStatesCollection"/> class
		/// that contains all the release states defined in the library.
		/// <remarks>The first release state in the collection is a special release state used when an object has no release state applied.</remarks>
		/// </summary>
		private void GetReleaseStates()
		{
			this.releaseStates = new Ait.Utilities.ReleaseStatesCollection();
			Ait.Dto.ReleaseState noReleaseState = new Ait.Dto.ReleaseState();
			noReleaseState.ReleaseStateId = 0;
			noReleaseState.Name = Resources.NoReleaseStateName;
			this.releaseStates.Add(noReleaseState);
			List<Ait.Dto.ReleaseState> relStateList = (List<Ait.Dto.ReleaseState>)svcReleaseState.GetAll();
			foreach (Ait.Dto.ReleaseState relState in relStateList)
			{
				this.releaseStates.Add(relState);
			}
		}

		/// <summary>
		/// The <see cref="GetVersionString()"/> method gets a <see cref="System.String"/> containing the version information for the plug-in.
		/// </summary>
		/// <returns>A <see cref="System.String"/> containing the version information for the plug-in.</returns>
		private string GetVersionString()
		{
			Version v = Assembly.GetExecutingAssembly().GetName().Version;
			string version = string.Format(CultureInfo.InvariantCulture, "{0}.{1}.{2}.{3}", v.Major, v.Minor, v.Build, v.Revision);
			return version;
		}

		/// <summary>
		/// The <see cref="LoadProperties()"/> method reads settings and resources and applies the values to properties on this instance of the <see cref="WordCountForm"/> class.
		/// </summary>
		private void LoadProperties()
		{
			this.Text = Resources.FormTitle;
			if (config.ShowVersion)
			{
				this.Text += String.Format(Resources.FormTitleVersionString, GetVersionString());
			}
			this.ShowIcon = config.FormShowIcon;
			if (this.ShowIcon)
			{
				this.Icon = Resources.word_count_icon;
			}
			this.dgvObjects.RowHeadersVisible = config.DataGridViewRowHeadersVisible;
			keepClipboardDataAfterExit = config.DataGridClipboardKeepDateAfterExit;
		}

		/// <summary>
		/// The <see cref="LoginLibrary()"/> method logs the current user into the Author-it library and
		/// sets the <see cref="Authorit.API.Services"/> classes to instances relevant to the current context.
		/// </summary>
		private void LoginLibrary()
		{
			appContext = Ait.Configuration.PluginUtility.GetAppContext(this.eventArgs);
			libraryAlias = appContext.LibrariesConfiguration[0].Alias;
			username = this.eventArgs.Username;
			password = this.eventArgs.Password;
			svcAuthentication = appContext.GetServiceInstance<Ait.Services.Authentication>();
			svcBook = appContext.GetServiceInstance<Ait.Services.Book>();
			svcFile = appContext.GetServiceInstance<Ait.Services.File>();
			svcHyperlink = appContext.GetServiceInstance<Ait.Services.Hyperlink>();
			svcObjectCommon = appContext.GetServiceInstance<Ait.Services.ObjectCommon>();
			svcReleaseState = appContext.GetServiceInstance<Ait.Services.ReleaseState>();
			svcTopic = appContext.GetServiceInstance<Ait.Services.Topic>();

			if (!svcAuthentication.IsLoggedIn())
			{
				Ait.Dto.LoginResponse response;
				if (String.IsNullOrEmpty(username))
				{
					response = svcAuthentication.LoginWithActiveDirectory(libraryAlias);
				}
				else
				{
					response = svcAuthentication.Login(libraryAlias, username, password);
				}
				bool loginSuccess = false;
				switch (response.Result)
				{
					case Authorit.API.Dto.LoginResult.FailureDatabaseNotFound:
						break;

					case Authorit.API.Dto.LoginResult.FailureError:
						break;

					case Authorit.API.Dto.LoginResult.FailureIncorrectUsernameOrPassword:
						break;

					case Authorit.API.Dto.LoginResult.FailureLibraryFutureVersion:
						break;

					case Authorit.API.Dto.LoginResult.FailureLibraryOpen:
						break;

					case Authorit.API.Dto.LoginResult.FailureLibraryUpgradeNeeded:
						break;

					case Authorit.API.Dto.LoginResult.FailureLicensing:
						break;

					case Authorit.API.Dto.LoginResult.Success:
						loginSuccess = true;
						break;

					default:
						break;
				}
				if (!loginSuccess)
				{
					MessageBox.Show(this, Resources.CaptionLoginFailure, Resources.MessageLoginFailure, MessageBoxButtons.OK, Settings.Default.IconLoginFailureMessage);
					this.Close();
				}
			}
		}

		/// <summary>
		/// The <see cref="menuCopy_Click(object, EventArgs)"/> method copies to the clipboard data from the <see cref="dgvObjects"/> DataGridView.
		/// </summary>
		/// <param name="sender">The object that initiated the event</param>
		/// <param name="e">Arguments for the event</param>
		private void menuCopy_Click(object sender, EventArgs e)
		{
			Clipboard.SetDataObject(dgvObjects.GetClipboardContent(), keepClipboardDataAfterExit);
		}

		/// <summary>
		/// The <see cref="menuSelectAll_Click(object, EventArgs)"/> method selects all rows in the <see cref="dgvObjects"/> DataGridView.
		/// </summary>
		/// <param name="sender">The object that initiated the event</param>
		/// <param name="e">Arguments for the event</param>
		private void menuSelectAll_Click(object sender, EventArgs e)
		{
			dgvObjects.SelectAll();
		}

		/// <summary>
		/// The <see cref="WordCountForm_Load(object, EventArgs)"/> method loads the <see cref="dgvObjects"/> DataGridView with
		/// information about the selected objects.
		/// </summary>
		/// <param name="sender">The object that initiated the event</param>
		/// <param name="e">Arguments for the event</param>
		private void WordCountForm_Load(object sender, EventArgs e)
		{
			objectIds = this.eventArgs.SelectedObjectIDs;

			LoginLibrary();
			GetReleaseStates();
			Ait.Dto.ObjectBrief selectedObject;
			if (this.bookId > 0)
			{
				objectIds.Clear();
				Ait.Dto.Book currentBook = svcBook.Get(this.bookId);
				Stack<List<Ait.Dto.BookTocEntry>> tocStack = new Stack<List<Ait.Dto.BookTocEntry>>();
				tocStack.Push((List<Ait.Dto.BookTocEntry>)currentBook.TableOfContents);
				while (tocStack.Count > 0)
				{
					List<Ait.Dto.BookTocEntry> currentTocEntries = tocStack.Pop();
					foreach (Ait.Dto.BookTocEntry currentEntry in currentTocEntries)
					{
						if (!objectIds.Contains(currentEntry.ObjectId))
						{
							objectIds.Add(currentEntry.ObjectId);
						}
						tocStack.Push((List<Ait.Dto.BookTocEntry>)currentEntry.Children);
					}
				}
			}
			foreach (int id in objectIds)
			{
				selectedObject = svcObjectCommon.GetObjectBrief(id);
				if (selectedObject.ObjectType == Ait.Dto.ObjectType.Topic)
				{
					int n = dgvObjects.Rows.Add();
					dgvObjects.Rows[n].Cells["ID"].ValueType = typeof(int);
					dgvObjects.Rows[n].Cells["ID"].Value = selectedObject.ObjectId;
					dgvObjects.Rows[n].Cells["Description"].ValueType = typeof(string);
					dgvObjects.Rows[n].Cells["Description"].Value = selectedObject.Description;

					dgvObjects.Rows[n].Cells["BasedOnId"].ValueType = typeof(int);
					dgvObjects.Rows[n].Cells["BasedOnId"].Value = selectedObject.BasedOnId;

					dgvObjects.Rows[n].Cells["BasedOnDescription"].ValueType = typeof(string);
					dgvObjects.Rows[n].Cells["BasedOnDescription"].Value = selectedObject.BasedOnDescription;

					dgvObjects.Rows[n].Cells["Template"].ValueType = typeof(bool);
					dgvObjects.Rows[n].Cells["Template"].Value = selectedObject.IsTemplate;
					dgvObjects.Rows[n].Cells["ReleaseState"].ValueType = typeof(string);
					dgvObjects.Rows[n].Cells["ReleaseState"].Value = releaseStates[selectedObject.ReleaseStateId].Name;
					if (releaseStates[selectedObject.ReleaseStateId].BackgroundColor != null)
					{
						dgvObjects.Rows[n].DefaultCellStyle.BackColor = releaseStates[selectedObject.ReleaseStateId].BackgroundColor;
					}
					if (releaseStates[selectedObject.ReleaseStateId].ForegroundColor != null)
					{
						dgvObjects.Rows[n].DefaultCellStyle.ForeColor = releaseStates[selectedObject.ReleaseStateId].ForegroundColor;
					}
					dgvObjects.Rows[n].Cells["ModifiedDate"].ValueType = typeof(DateTime);
					dgvObjects.Rows[n].Cells["ModifiedDate"].Value = selectedObject.ModifiedDate;
					dgvObjects.Rows[n].Cells["ModifiedBy"].ValueType = typeof(string);
					dgvObjects.Rows[n].Cells["ModifiedBy"].Value = selectedObject.ModifiedBy;
					string topicText = Ait.Configuration.PluginUtility.GetTopicText(selectedObject.ObjectId, svcTopic);
					int wordCount = Ait.Configuration.PluginUtility.GetWordCount(topicText);
					dgvObjects.Rows[n].Cells["WordCount"].ValueType = typeof(int);
					dgvObjects.Rows[n].Cells["WordCount"].Value = wordCount;
					dgvObjects.Rows[n].Cells["WordCountResolved"].ValueType = typeof(int);
					if (this.bookId > 0)
					{
						string resolvedTopicText = svcTopic.GetResolvedTopicTextXml(selectedObject.ObjectId, this.bookId);
						resolvedTopicText = Ait.Configuration.PluginUtility.GetTopicText(resolvedTopicText);
						dgvObjects.Rows[n].Cells["WordCountResolved"].Value = Ait.Configuration.PluginUtility.GetWordCount(resolvedTopicText);
					}
					else
					{
						dgvObjects.Rows[n].Cells["WordCountResolved"].Value = -1;
					}
				}
			}
			if (dgvObjects.Rows.Count < 1)
			{
				MessageBox.Show(this, Resources.MessageNoObjectSelected, Resources.CaptionNoObjectSelected, MessageBoxButtons.OK, Settings.Default.IconNoObjectSelectedMessageBox);
				this.Close();
			}
			else if (config.DataGridShowTotalRow)
			{
				int sumWordCount = 0;
				int sumWordCountResolved = 0;
				for (int i = 0; i < dgvObjects.Rows.Count; i++)
				{
					sumWordCount += (int)dgvObjects.Rows[i].Cells["WordCount"].Value;
					if (bookId > 0)
					{
						sumWordCountResolved += (int)dgvObjects.Rows[i].Cells["WordCountResolved"].Value;
					}
					else
					{
						sumWordCountResolved = -1;
					}
				}
				int n = dgvObjects.Rows.Add();
				dgvObjects.Rows[n].Cells["Description"].ValueType = typeof(string);
				dgvObjects.Rows[n].Cells["Description"].Value = Resources.TotalRowDescription;
				dgvObjects.Rows[n].Cells["WordCount"].ValueType = typeof(int);
				dgvObjects.Rows[n].Cells["WordCount"].Value = sumWordCount;
				dgvObjects.Rows[n].Cells["WordCountResolved"].ValueType = typeof(int);
				dgvObjects.Rows[n].Cells["WordCountResolved"].Value = sumWordCountResolved;
			}
		}

		#endregion Methods
	}
}